from django.shortcuts import render
from rest_framework.decorators import api_view
from django.http import JsonResponse, HttpResponse, FileResponse
from django.core.paginator import Paginator
from django.db.models import Q
from .models import ApiCase, ApiReport
import json
import os
import uuid
import yaml
import subprocess
import shutil
from datetime import datetime
from pathlib import Path
from django.conf import settings
from mango_service.tools import is_blank

# 定义基础路径
BASE_DIR = Path(settings.BASE_DIR)
HTTPRUNNER_DIR = BASE_DIR / 'HttpRunner'
TESTCASE_DIR = HTTPRUNNER_DIR / 'testcase'
REPORT_DIR = BASE_DIR / 'static' / 'apiReports'  # 报告存储目录
ALLURE_BIN = Path('D:/allure/allure-2.33.0/bin/allure.bat')  # Allure路径

# 确保目录存在
TESTCASE_DIR.mkdir(parents=True, exist_ok=True)
REPORT_DIR.mkdir(parents=True, exist_ok=True)


def get_relative_path(absolute_path):
    """将绝对路径转换为相对于项目根目录的相对路径"""
    try:
        return Path(absolute_path).relative_to(BASE_DIR).as_posix()
    except ValueError:
        return absolute_path


def get_absolute_path(relative_path):
    """将相对路径转换为绝对路径"""
    return BASE_DIR / relative_path


def get_report_url(report_path):
    """获取报告的URL访问路径"""
    if not report_path:
        return None

    # 报告路径格式: static/apiReports/<report_id>/html/index.html
    # 提取报告目录名
    report_dir = Path(report_path).parent.parent

    # 构建URL路径 - 返回相对路径
    relative_path = report_dir.relative_to(BASE_DIR / 'static')
    return f"/static/{relative_path}/html/index.html"


@api_view(["GET"])
def api_list(request):
    data = {}
    case_name = request.GET.get("case_name")
    functional = request.GET.get("functional")
    status = request.GET.get("status")
    page = request.GET.get('page')
    pagesize = request.GET.get('pagesize')

    # 构建查询参数
    if not is_blank(case_name):
        data["case_name__icontains"] = case_name
    if not is_blank(functional):
        data["functional"] = functional
    if not is_blank(status):
        data["status"] = status

    # 查询用例列表
    cases = ApiCase.objects.filter(**data).order_by('-create_time')
    total = cases.count()

    # 处理分页逻辑
    if is_blank(page) or int(page) < 1:
        page = 1
    else:
        page = int(page)

    if is_blank(pagesize):
        pagesize = 10
    else:
        pagesize = int(pagesize)

    paginator = Paginator(cases, pagesize)

    # 处理页码超出范围的情况
    if page > paginator.num_pages:
        page = paginator.num_pages if paginator.num_pages > 0 else 1

    case_data = paginator.page(page)

    # 构建返回数据
    info = []
    for case in case_data.object_list:
        case_info = {
            'id': case.id,
            'case_name': case.case_name,
            'case_path': case.case_path,
            'create_time': case.create_time.strftime('%Y-%m-%d %H:%M:%S'),
            'execute_time': case.execute_time.strftime('%Y-%m-%d %H:%M:%S') if case.execute_time else None,
            'status': case.status,
            'functional': case.functional,
            'report_create_time': None,
            'report_path': None,
            'report_url': None,  # 新增报告URL字段
        }

        # 查询关联的最新报告
        try:
            report = ApiReport.objects.filter(case_id=case.id).latest('create_time')
            case_info['report_create_time'] = report.create_time.strftime('%Y-%m-%d %H:%M:%S')
            case_info['report_path'] = report.report_path
            case_info['report_url'] = get_report_url(report.report_path)  # 添加可访问的URL
        except ApiReport.DoesNotExist:
            pass

        info.append(case_info)

    # 计算总页数
    total_pages = paginator.num_pages

    return JsonResponse({
        'status': 200,
        'message': '查询成功',
        'data': info,
        'total': total,
        'page': page,
        'pagesize': pagesize,
        'total_pages': total_pages
    })


@api_view(["POST"])
def upload_api_case(request):
    if 'file' not in request.FILES:
        return JsonResponse({'status': 400, 'message': '未上传文件'}, status=400)

    file = request.FILES['file']
    case_name = request.POST.get('case_name')
    functional = request.POST.get('functional')

    if not case_name:
        return JsonResponse({'status': 400, 'message': '用例名称不能为空'}, status=400)

    # 生成唯一文件名
    unique_id = uuid.uuid4().hex
    file_ext = os.path.splitext(file.name)[1]
    filename = f"{unique_id}{file_ext}"
    filepath = TESTCASE_DIR / filename

    # 保存文件
    with open(filepath, 'wb+') as destination:
        for chunk in file.chunks():
            destination.write(chunk)

    # 获取相对路径并存入数据库
    relative_path = get_relative_path(filepath)

    # 创建用例记录
    api_case = ApiCase.objects.create(
        case_name=case_name,
        case_path=relative_path,
        functional=functional,
        status='0'
    )

    return JsonResponse({
        'status': 200,
        'message': '上传成功',
        'data': {
            'id': api_case.id,
            'case_name': api_case.case_name,
            'case_path': api_case.case_path,
            'create_time': api_case.create_time.strftime('%Y-%m-%d %H:%M:%S')
        }
    })


@api_view(["POST"])
def execute_api_case(request, case_id):
    try:
        api_case = ApiCase.objects.get(id=case_id)
    except ApiCase.DoesNotExist:
        return JsonResponse({'status': 404, 'message': '用例不存在'}, status=404)

    # 获取用例绝对路径
    case_path = get_absolute_path(api_case.case_path)

    # 创建唯一报告目录
    report_id = uuid.uuid4().hex
    report_dir = REPORT_DIR / report_id
    data_dir = report_dir / 'data'  # 测试数据目录
    html_dir = report_dir / 'html'  # HTML报告目录

    try:
        # 执行测试用例
        hrun_cmd = f'httprunner run "{api_case.case_path}" --alluredir="{data_dir}"'
        if os.system(hrun_cmd) != 0:
            raise Exception("测试用例执行失败")

        # 生成报告
        allure_cmd = f'allure generate "{data_dir}" -o "{html_dir}"'
        if os.system(allure_cmd) != 0:
            raise Exception("报告生成失败")

        # 更新用例状态
        api_case.status = '1'  # 已执行
        api_case.execute_time = datetime.now()
        api_case.save()

        # 创建报告记录
        report_path = html_dir / 'index.html'
        report = ApiReport.objects.create(
            report_path=str(report_path),
            case=api_case
        )

        # 返回成功响应
        return JsonResponse({
            'status': 200,
            'message': '执行成功',
            'data': {
                'report_id': report.id,
                'report_url': get_report_url(str(report_path))
            }
        })

    except Exception as e:
        # 清理失败的报告目录
        if report_dir.exists():
            shutil.rmtree(report_dir)

        return JsonResponse({
            'status': 500,
            'message': '执行失败',
            'error': str(e)
        }, status=500)


@api_view(["DELETE"])
def delete_api_case(request, case_id):
    try:
        api_case = ApiCase.objects.get(id=case_id)
    except ApiCase.DoesNotExist:
        return JsonResponse({'status': 404, 'message': '用例不存在'}, status=404)

    # 删除关联的报告
    reports = ApiReport.objects.filter(case=api_case)
    for report in reports:
        # 删除报告目录
        report_path = Path(report.report_path)
        if report_path.exists():
            # 删除报告文件
            if report_path.is_file():
                report_path.unlink()

            # 删除报告目录
            report_dir = report_path.parent
            if report_dir.exists():
                shutil.rmtree(report_dir)
        report.delete()

    # 将相对路径转换为绝对路径删除用例文件
    case_absolute_path = get_absolute_path(api_case.case_path)
    if os.path.exists(case_absolute_path):
        os.remove(case_absolute_path)

    # 删除用例记录
    api_case.delete()

    return JsonResponse({'status': 200, 'message': '删除成功'})


@api_view(["GET"])
def download_api_case(request, case_id):
    try:
        api_case = ApiCase.objects.get(id=case_id)
    except ApiCase.DoesNotExist:
        return JsonResponse({'status': 404, 'message': '用例不存在'}, status=404)

    # 将相对路径转换为绝对路径
    case_absolute_path = get_absolute_path(api_case.case_path)

    if not os.path.exists(case_absolute_path):
        return JsonResponse({'status': 404, 'message': '用例文件不存在'}, status=404)

    file_path = Path(case_absolute_path)
    file_name = f"{api_case.case_name}{file_path.suffix}"

    response = FileResponse(open(file_path, 'rb'))
    response['Content-Disposition'] = f'attachment; filename="{file_name}"'
    return response
